package org.ww.config;

import org.aspectj.lang.annotation.Before;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.ww.aop.LogAspects;
import org.ww.aop.MathCalculator;

/**
 * AOP切面编程
 *      指的是在程序运行期间动态的将某段代码切入到指定位置并进行编程方式
 * 1、导入aop模块 （spring-aspects)
 * 2、定义一个业务逻辑类（MathCalculator)，在业务逻辑运行的时候将日志打印（方法之前、方法运行后，方法报错）
 * 3、定义一个日志切面类（LogAspects)，切面里边的方法需要动态的感知MathCalculator.div方法运行到哪里
 *        前置通知@Before：logStart：在方法div运行之前执行
 *        后置通知@After: logEnd：在方法div运行之后执行
 *        返回通知@AfterReturning: logReturn：在方法正常返回之后执行
 *        异常通知@AfterThrowing: logException：在方法异常后执行
 *        环绕通知@Around:动态代理，手动推动目标方法运行，
 * 4、给切面类的方法标注何时何地（通知）
 * 5、将切面类和业务逻辑类注册到容器中
 * 6、表明哪个类是切面类  @Aspect
 * [7]、给配置类中加@EnableAspectJAutoProxy（开启基于注解的aop模式）
 *
 * 自动装配原理：
 *      （1）看给容器中加入了什么组件
 *      （2）查看该组件能够实现什么功能，在什么时候实现
 * AOP原理：
 *      @EnableAspectJAutoProxy开启aop模式
 *
 *   1   导入了@Import(AspectJAutoProxyRegistrar.class)
 *       AspectJAutoProxyRegistrar implement ImportBeanDefinitionRegistrar
 *       ImportBeanDefinitionRegistrar：给容器中加入自定义组件
 *       给容器中加入了组件AnnotationAwareAspectJAutoProxyCreator
 *       internalAutoProxyCreator=AnnotationAwareAspectJAutoProxyCreator
 *
 *   2   AnnotationAwareAspectJAutoProxyCreator
 *          -> AspectJAwareAdvisorAutoProxyCreator
 *                -> AbstractAdvisorAutoProxyCreator
 *                      -> AbstractAutoProxyCreator
 *                        implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware
 *                  （BeanPostProcessor：后置处理器，在注册容器前和后做一些事情）
 *                  BeanFactoryAware:自动装配BeanFactory
 *
 *   3
 *          AbstractAutoProxyCreator.setBeanFactory()
 *          AbstractAutoProxyCreator.postProcessBeforeInstantiation()后置处理器相关方法
 *
 *          AbstractAdvisorAutoProxyCreator.setBeanFactory => initBeanFactory
 *
 *          AnnotationAwareAspectJAutoProxyCreator.initBeanFactory
 *
 *  流程：
 *      1、传入配置类，创建ioc容器，
 *      2、执行refresh()方法，刷新容器
 *      3、registerBeanPostProcessors(beanFactory)注册bean的后置处理器方便拦截bean进行注册
 *          1）先获取容器中已经定义的需要创建对象的所有BeanPostProcessor
 *          2）给容器中加其他的BeanPostProcessor
 *          3）优先注册实现了PriorityOrdered的BeanPostProcessor
 *          4）在注册实现了Ordered接口的BeanPostProcessor
 *          5）注册没有实现接口的所有BeanPostProcessors
 *          6）注册名字为internalAutoProxyCreator的类型为AnnotationAwareAspectJAutoProxyCreator的bean
 *              1）创建Bean的实例
 *              2）populateBean给bean的各种属性赋值
 *              3）initializeBean初始化实例bean
 *                  1）invokeAwareMethods：处理Aware接口的回调
 *                  2）applyBeanPostProcessorsBeforeInitialization
 *                     应用后置处理器postProcessBeforeInitialization
 *                  3）invokeInitMethods：执行初始化方法
 *                  4）applyBeanPostProcessorsBeforeInitialization
 *                     应用后置处理器postProcessAfterInitialization
 *              4）BeanPostProcessor[AnnotationAwareAspectJAutoProxyCreator]创建成功
 *     =======================AnnotationAwareAspectJAutoProxyCreator后置处理器创建成功=====================================
 *                 AnnotationAwareAspectJAutoProxyCreator => InstantiationAwareBeanPostProcessor
 *      4、finishBeanFactoryInitialization(beanFactory)初始化其他的BeanPostProcessor
 *          1）依次遍历容器中所有bean
 *          getBean->doGetBean->getSingleton
 *          2）创建bean
 *              1）先从缓存中获取当前bean,如果能够获取到，说明是之前所创建得，直接使用，否则创建
 *              只要创建好的bean都会被加载到缓存中
 *              2）createBean(beanName, mbd, args)创建bean
 *                  【BeanPostProcessor是在完成bean初始化前后执行后置处理器】
 *                  【InstantiationAwareBeanPostProcessor是在创建bean之前尝试用后置处理器返回对象】
 *                  1）resolveBeforeInstantiation(beanName, mbdToUse)解析BeforeInstantiation
 *                    希望后置处理器在此返回一个代理对象，如果能返回就使用，如果不能就创建
 *                          1）先尝试让bean返回对象
 *                              bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
 *                                  拿到所有得后置处理器，如果类型是InstantiationAwareBeanPostProcessor
 *                                  就执行postProcessBeforeInstantiation
 * 					            if (bean != null) {
 * 					            	bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
 * 					            }
 *                  2）doCreateBean(beanName, mbdToUse, args)这才是真正得创建bean实例，和3.6流程一样
 *=========================================================================================
 *  【MathCalculator】
 *  【LogAspects】
 *  在创建对象之前和之后做了哪些事情：
 *  1、创建对象之前调用了postProcessBeforeInstantiation方法
 *      1、isInfrastructureClass是否包含一些基本类：Aspect、Advice、Pointcut、Advisor、AopInfrastructureBean
 *      2、shouldSkip：findCandidateAdvisors获取所有的候选增强器，遍历查看当前增强器是否是AspectJPointcutAdvisor（当前增强器InstantiationModelAwarePointcutAdvisor）
 *      在前期主要是判断，具体没有做什么事
 *  2、创建MathCalculator对象
 *  3、创建对象之后调用postProcessAfterInitialization
 *      1、调用方法wrapIfNecessary：包装代理对象
 *          1、getAdvicesAndAdvisorsForBean：获取所有的增强器
 *          2、createProxy：创建代理对象：
 *              获取所有的增强器（通知方法）：将增强器放入proxyFactory中
 *              proxyFactory.getProxy：获取代理对象
 *                  getAopProxyFactory().createAopProxy：创建aop代理对象
 *                      创建代理对象Spring自动决定
 *                          new ObjenesisCglibAopProxy(config) : new JdkDynamicAopProxy(config));
 *          3、给当前容器中返回cglib增强了的代理对象
 *          4、以后容器中获取到的就是这个增强了的代理对象，执行目标方法时，代理对象就会执行通知方法的流程
 *
 *  ==============================目标拦截链========================================
 *    1、调用目标方法之前执行intercept方法，在方法里获取拦截器链
 *      List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
 *          1、遍历所有的增强器，然后将封装成一个interceptorList
 *          2、将增强器转为list
 *             如果为MethodInterceptor，直接加入到集合中
 *             如果不是，使用AdvisorAdapter将其转为MethodInterceptor
 *             转换完之后返回MethodInterceptor数组
 *    2、如果获取不到，就直接执行目标方法
 *       拦截器链（每一个通知方法又被包装为方法拦截器，利用MethodInterceptor机制）
 *    3、如果获取到了，就传入一系列的参数执行CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed()
 *    4、拦截器链的触发过程
 *      1、执行proceed方法
 *          this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size()
 *          判断当前拦截器索引是否和拦截器链列表长度一致
 *          ++this.currentInterceptorIndex：依次获取当前递增的索引
 *          获取完成后执行invoke方法，在invoke方法里又会调用proceed方法，直到遇见this.advice.before执行前置通知方法
 *          最后执行后置通知方法，然后异常通知方法
 *
 */

@EnableAspectJAutoProxy
@Configuration
public class MainConfigOfAOP {
    @Bean
    public MathCalculator mathCalculator(){
        return new MathCalculator();
    }

    @Bean
    public LogAspects logAspects(){
        return new LogAspects();
    }
}
